In [1]:
import plotly.graph_objects as go
import numpy as np
import math
pi = math.pi

This is equation that describes the ultimate string vibrations:

$$\dfrac{\partial^2 u}{\partial t^2}=\dfrac{\partial^2 u}{\partial x^2},\quad t>0,~0<x<1$$

So we have initial conditions: $$u\big|_{t=0}=x(1-x),\quad \dfrac{\partial u}{\partial x}\bigg|_{t=0}=0,$$ This means that in d'Alembert formula: $$\int\limits_{x-at}^ {x+at} \psi(\alpha) d\alpha = 0$$ Lets find: $ \varphi$

To satisfy the boundary conditions, it is necessary to impose parity

conditions (because we have lemma for the Neumann conditions) with respect to $ x \in [0:l].$

Then : $$\varphi_1(-x)=\varphi_1(x)=\varphi_1(2l-x)$$ Lets: $$x_1=-x$$ So, $$\varphi_1(x_1)=\varphi_1(x_1+2l)$$ It automatically follows from here : $$T = 2l$$ Thus, the parity conditions are relatively the origin and periodicity conditions determine the continuation $\varphi(x)$

In [2]:
def fi(x : int) -> int:
    return x % 1 * (1 - x % 1)

Substitute in the d'Alembert formula:

In [3]:
def U(pull_of_x,t : int, a: int) -> list:
    res = []
    for x in pull_of_x:
        res.append((fi(x+a*t)+fi(x-a*t))/2)
    return res

I chose a segment [0:1] because of initial conditions, a = 1 too.

In [4]:
a=1
begin_of_x = 0
end_of_x = 1
step= 0.005
pull_of_x= np.arange(begin_of_x,end_of_x,step)
In [6]:
stack = []
step_t =0.01
T = 10
for t in np.arange(0,T,step_t):
    stack.append(go.Frame(data=go.Scatter(x=pull_of_x, y=U(pull_of_x,t,a))))
In [8]:
go.Figure(data=go.Scatter(x=pull_of_x, y=U(pull_of_x,0,a)),layout=go.Layout(
        xaxis=dict(range=[begin_of_x, end_of_x], autorange=False),
        yaxis=dict(range=[-2, 2], autorange=False),
        title="Vibrating String with free ends",
        updatemenus=[dict(
            type="buttons",
            buttons=[dict(label="Play",
                          method="animate",
                          args=[None])])]
    ),frames=stack)